package com.googlecode.mapperdao

import jdbc.{Transaction, Setup}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{Matchers, FunSuite}
import utils.{TransactionalNaturalStringIdCRUD, TransactionalSurrogateIntIdCRUD}
import org.springframework.transaction.PlatformTransactionManager
import java.util.UUID._

@RunWith(classOf[JUnitRunner])
class UseCaseCyclicDependenciesSuite extends FunSuite with Matchers
{
	if (Setup.database == "postgresql") {

		def makeUUID = randomUUID.toString.toUpperCase

		case class BusDriver(name: String)

		case class People(drivers: Set[BusDriver], name: Option[String] = None)

		case class Visit(id: String, people: People)

		case class Commute(id: String, name: String, people: People, visits: Set[Visit])

		object BusDriverEntity extends Entity[Int, SurrogateIntId, BusDriver]("BusDriver")
		{
			val id = key("id") autogenerated (_.id)
			val name = column("name") to (_.name)

			def constructor(implicit m: ValuesMap) = new BusDriver(name) with SurrogateIntId
			{
				val id: Int = BusDriverEntity.id
			}
		}

		class BusDriverDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager) extends TransactionalSurrogateIntIdCRUD[BusDriver]
		{
			val entity = BusDriverEntity
		}

		object PeopleEntity extends Entity[Int, SurrogateIntId, People]("People")
		{
			val id = key("id") autogenerated (_.id)
			val name = column("name") option (_.name)
			val drivers = manytomany(BusDriverEntity) to (_.drivers)

			def constructor(implicit m: ValuesMap) = new People(drivers) with SurrogateIntId
			{
				val id: Int = PeopleEntity.id
			}

		}

		class PeopleDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager) extends TransactionalSurrogateIntIdCRUD[People]
		{
			val entity = PeopleEntity
		}

		object VisitEntity extends Entity[String, NaturalStringId, Visit]("Visit")
		{
			val id = key("id") to (_.id)
			val people = manytoone(PeopleEntity) to (_.people)

			def constructor(implicit m: ValuesMap) = new Visit(id, people) with NaturalStringId

		}

		class VisitDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager) extends TransactionalNaturalStringIdCRUD[Visit]
		{
			val entity = VisitEntity
		}

		object CommuteEntity extends Entity[String, NaturalStringId, Commute]("Commute")
		{
			val id = key("id") to (_.id)
			val name = column("name") to (_.name)
			val people = manytoone(PeopleEntity) to (_.people)
			val visits = manytomany(VisitEntity) to (_.visits)

			def constructor(implicit m: ValuesMap) = new Commute(id, name, people, visits) with NaturalStringId
		}

		class CommuteDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager) extends TransactionalNaturalStringIdCRUD[Commute]
		{
			val entity = CommuteEntity
		}

		val (jdbc, mapperDao, queryDao) = Setup.setupMapperDao(List(BusDriverEntity, PeopleEntity, VisitEntity, CommuteEntity))
		val transactionManager = Transaction.transactionManager(jdbc)

		val busDriverDao = new BusDriverDao(mapperDao, queryDao, transactionManager)
		val peopleDao = new PeopleDao(mapperDao, queryDao, transactionManager)
		val visitDao = new VisitDao(mapperDao, queryDao, transactionManager)
		val commuteDao = new CommuteDao(mapperDao, queryDao, transactionManager)


		test("CRUD with existing entities") {
			createTables()
			val driver1 = busDriverDao.create(BusDriver("driver1"))
			val people1 = peopleDao.create(People(Set(driver1)))

			val visit = visitDao.create(Visit(makeUUID, people1))
			val commuter = visitDao.create(Visit(makeUUID, people1))

			val commute = commuteDao.create(Commute(makeUUID, "commute", people1, Set(visit, commuter)))
			val loaded = commuteDao.retrieve(commute.id).get
			loaded should be(commute)

			val up1 = loaded.copy(
				people = loaded.people.copy(drivers = loaded.people.drivers + BusDriver("driver2"))
			)
			val u1 = commuteDao.update(loaded, up1)
			u1 should be(up1)
			commuteDao.retrieve(commute.id).get should be(up1)

			commuteDao.delete(commute.id)

			commuteDao.retrieve(commute.id) should be(None)
		}

		test("persists with new entities") {
			createTables()
			val driver1 = BusDriver("driver1")
			val people1 = People(Set(driver1))

			val visit = Visit(makeUUID, people1)
			val commuter = Visit(makeUUID, people1)

			val commute = commuteDao.create(Commute(makeUUID, "commute", people1, Set(visit, commuter)))
			val loaded = commuteDao.retrieve(commute.id).get
			loaded should be(commute)
		}

		def createTables() {
			Setup.dropAllTables(jdbc)
			val queries = Setup.queries(this, jdbc)
			queries.update("ddl")
		}
	}
}
